// 我们会获取到多个用户选择的文件，我们需要将这些文件保存起来，最终传递给后端。
let fileListMap = new Map();

/******************  获取页面元素 start  **********************/
// 获取上传区域
let uploadSection = document.getElementById("uploadSection");
// 获取文件列表
let fileList = document.getElementById("fileList");
// 获取删除所有按钮
let clearAllBtn = document.getElementById("clearAllBtn");
// 获取文件上传按钮
let uploadAllBtn = document.getElementById("uploadAllBtn");
// 获取页面展示的文件上传按钮（好看的）
let selectFileBtn = document.getElementById("selectFileBtn");
// 获取隐藏的文件上传按钮（丑的）
let fileInput = document.getElementById("fileInput");
/******************  获取页面元素 end  **********************/

// 事件绑定
selectFileBtn.addEventListener("click",function(e){
    fileInput.click();
})
// 对文件上传按钮做内容改变事件，当内容改变后。我们将提取的文件传递到添加到列表的方法。
fileInput.addEventListener("change",function (e){
    // 获取到用户选择的文件
    const files = e.target.files;
    // 将获取的文件调用方法，添加到列表中
    handleFiles(files)
})
// 文件拖拽到上传区域（拖拽中）。
uploadSection.addEventListener("dragover",function(e){
    // 阻止默认行为
    e.preventDefault();
    // 添加文件区域的样式，表示拖拽进去了
    uploadSection.classList.add("dragover")
})
// 拖拽文件离开上传区域
uploadSection.addEventListener("dragleave",function (e){
    // 阻止默认行为
    e.preventDefault();
    // 移除文件区域的样式，表示拖拽出去了
    uploadSection.classList.remove("dragover")
})
// 拖拽到上传区域后松手（选择文件）
uploadSection.addEventListener("drop",function (e){
    // 阻止默认行为
    e.preventDefault();
    // 移除文件区域的样式，表示准备文件上传了
    uploadSection.classList.remove("dragover")
    // 获取到用户拖拽进入的文件，支持多文件上传。所以获取的一定是一个列表（数组、集合）
    const files = e.dataTransfer.files
    // 获取到文件后我们就需要将文件添加到文件列表中展示给用户看了。我们可以在做一个方法来处理这个请求。
    handleFiles(files)
})

// 将文件添加到文件列表中展示到页面
function handleFiles(files){
    // files 转换为数组，这样就可以遍历，就可以获取到每一个文件
    Array.from(files).forEach(function (file){
        // console.log(file)
        // 定义指定类型格式  PDF、Word、TXT
        const types = ['application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/pdf','text/plain']
        // 判断文件格式必须在指定格式内。 includes 包含。
        // 验证 file.type 属性是否在 types 数组中存在。
        if(!types.includes(file.type)){
            Swal.fire({
                title: `文件 ${file.name} 类型不支持`, //显示内容
                text:`请上传文件类型：PDF、Word、TXT的文件`,
                icon:'error'
            })
            // 类型错误就不需要继续执行了。直接结束该方法。
            return;
        }
        // 拿到每个文件验证大小 10 * 1024 * 1024 = 10MB 不能超过
        if(file.size > 10 * 1024 * 1024){
            Swal.fire({
                title: `文件 ${file.name} 大小超过了 10MB`, //显示内容
                text:`请重新上传`,
                icon:'error'
            })
            return;
        }
        // 将内容添加到上传列表中了。
        // 上传的文件需要向后台传递。 那我们需要将文件的信息封装到  fileListMap 中
        // 若文名件不在 fileListMap 中。说明不能重名可以添加
        if(!fileListMap.has(file.name)){
            // 不重复则将文件添加到 fileListMap 中。准备向后台传递。
            // Map<Key,Value>  Key = "文件名"   Value = "文件内容"
            fileListMap.set(file.name,file);
            // 将信息展示到页面中。
            const fileItem = document.createElement('div');
            fileItem.className = 'file-item';
            fileItem.innerHTML = `
                 <div class="file-name">${file.name}</div>
                 <div class="file-size">${formatFileSize(file.size)}</div>
                 <div class="file-status status-pending">待上传</div>
                 <div class="remove-file" onclick="removeFile('${file.name}')">
                      <i class="bi bi-x-circle"></i>
                 </div>
                 <div class="upload-progress">
                      <div class="progress-bar" id="progress-${file.name}"></div>
                 </div>
             `
            // 将上面生成 DIV 模板添加到显示列表页面中。
            fileList.appendChild(fileItem);
            // 将删除所有按钮展示出来
            clearAllBtn.style.display = "block"
        } else {
            // 重名了（上传了重名文件）
            Swal.fire({
                title: `文件 ${file.name} 名已存在`, //显示内容
                text:`请重新上传`,
                icon:'error'
            })
            return;
        }

    })
}
// 大小格式化（后面添加B KB MB）
function formatFileSize(size){
    if(size < 1024){
        return size + "B"
    }
    else if(size < (1024 * 1024)){
        return (size/1024).toFixed(2) + "KB"
    }
    else {
        return (size/(1024 * 1024)).toFixed(2) + "MB"
    }
}
// 删除所有
clearAllBtn.addEventListener("click",function(){
// 重名了（上传了重名文件）
    Swal.fire({
        icon: 'info', // 弹框类型
        title: '您确定要清空上传列表吗？', //标题
        text: "清空后将无法恢复，请谨慎操作！", //显示内容
        confirmButtonColor: '#3085d6',// 确定按钮的 颜色
        confirmButtonText: '确定',// 确定按钮的 文字
        showCancelButton: true, // 是否显示取消按钮
        cancelButtonColor: '#d33', // 取消按钮的 颜色
        cancelButtonText: "取消", // 取消按钮的 文字
    }).then((isConfirm) => {
        if(isConfirm.value){
            // 当点击了确定按钮，要清空我们存储数据的集合
            fileListMap.clear();
            // 需要清空 HTML 页面中文件列表的 DIV
            fileList.innerHTML = "";
            // 隐藏清除所有文件按钮
            clearAllBtn.style.display = 'none';
        }
    });
})
// 删除单个文件
function removeFile(fileName){
    // console.log(fileName)
    // fileListMap 中存储数据的格式    Map<Key,Value>  Key = "文件名"   Value = "文件内容"
    // Key = aa.txt  Value = "文件对象"
    // 可以根据 Key 去删除 Value
    // 在 fileListMap 数据集合中将当前这条数据根据 Key 将整条数据删除
    fileListMap.delete(fileName)
    // 将页面中当前这条 DIV 元素删除
    // Array.from(fileList.children)  找到  fileList(文件列表) 内的所有元素
    // .find  查找
    // item -->  临时变量名，表示每次遍历出来 fileList 的内部元素。
    const deleteDiv = Array.from(fileList.children).find(item =>
        item.querySelector(".file-name").innerHTML === fileName)
    if(deleteDiv){
        // 找到了需要删除的 DIV。将该 DIV 删除掉
        deleteDiv.remove();
    }
    // 我们需要判断，若文件是最后一条，则删除后需要将删除所有按钮隐藏
    if(fileListMap.size === 0){
        // 没有任何数据了
        clearAllBtn.style.display = 'none';
    }
}
// 文件上传
uploadAllBtn.addEventListener("click",uploadAllFiles);
// 实现文件上传
async function uploadAllFiles(){
    // 判断上传文件的集合中不能为 Null， 长度不能小于 0
    if(fileListMap.size < 0){
        Swal.fire({
            text:`别忽悠我，没文件`,
            icon:'error'
        })
        return;
    }
    // 在上传过程中，我们需要更改按钮样式。让其变为禁用。这样可以防止用户误点击。
    uploadAllBtn.disabled = true;
    // 更改在上传过程中按钮的样式。
    uploadAllBtn.innerHTML = '<span class="spinner-border spinner-border-sm"  ></span> 上传中...'
    // 准备文件上传。 若做文件上传需要使用一个 FormData 的对象。他内部是 Key --> Value 键值对模式，不会影响文件的数据。
    // 大家可以简单的理解为文件上传就使用  FormData 的对象就可以了。
    const formData = new FormData();
    /**
     * Key --> Value
     * key  file
     * Value  文件对象（流）
     */
    // 遍历 fileListMap 集合，将内部的每一个文件都遍历出来，依次存储到 formData 对象中。
    // fileListMap<fileName,file>
    for(const [fileName,file] of fileListMap){
        formData.append("file",file);
    }
    // 将文件信息都封装到 formData 内。我们就可以使用 formData 作为参数实现文件上传了。
    try{
        // 没有产生异常的代码
        // 验证用户是否登录，若没有登录则不让实现文件上传。
        // 在 localStorage 中获取 token
        const token = localStorage.getItem("token");
        // 若没获取到 token 则说明没有登录。  一脚踢回登录页面。
        if(!token){
            Swal.fire({
                text:`请先登录在进行文件上传！`,
                icon:'warning'
            })
            window.location.href = "/";
            return;
        }
        // 异步访问后端，实现文件上传向量数据库。
        const response = await fetch(
            '/api/auth/upload', // 请求路径
            {
                method:"POST",      // 文件上传必须使用 POST 请求。
                body:formData       // 封装好的而文件参数
            }
        )
        /**
         * 后台返回的数据
         * status  === 200  成功    其他都是失败。
         *    文件名   上传成功 换行   中间3个空格
         * 文件名   上传失败 换行
         */
        if(response.status === 200){
            // response.text()  返回的文本信息。 文件名   上传成功   中间3个空格 || 文件名   上传失败 。有几个文件返回几组。
            const responseData = await response.text();
            // 按照换行切分。 且分出每一个文件的状态和文字。
            const messages = responseData.split("\n")
            // 切分后遍历数据。（每次切分都会得到一个文件。）
            messages.forEach(message => {
                // 得到每一行的消息后去掉前后多余的空格
                // 文件名   上传成功
                // 文件名   上传成功
                if (message.trim()) {
                    // 按照  中间的3个空格再次切分。切分后得到一个数组
                    let temp = message.split("   ");
                    // ["文件名","上传成功"]
                    // ["文件名","上传成功"]
                    // 数组中第一个存储的就是文件名字
                    const fileName = temp[0]
                    // 数组中第二个存储的就是上传成功或者上传失败
                    // temp[1].includes("成功") ? "success" : "error";  若temp[1] 中包含成功 则为 success 不包含则为 error
                    const status = temp[1].includes("成功") ? "success" : "error";
                    // 写一个更新状态的方法。
                    uploadFileStatus(fileName, status, temp[1].trim());
                }
            })
        } else {
                // 失败
                // 调用更新状态的方法
                uploadFileStatus(fileName,'error','上传失败');
            }
    } catch(e){
        // 产生了异常的代码
        // 调用更新状态的方法
        Swal.fire({
            text:`文件上传产生了异常！请稍后再试！`,
            icon:'error'
        })
    } finally {
        // 无论是否出现异常都会执行的代码
        // 将开始上传按钮恢复为初始状态
        uploadAllBtn.disabled = false;
        uploadAllBtn.innerHTML = "<i class=\"bi bi-cloud-upload\"></i> 开始上传";
    }
}
function uploadFileStatus(fileName,status,message){
    // 找到 fileList(文件列表的DIV) 的所有孩子。遍历得到 class = file-name。拿到名字与其相同的，这个就是我们要改变状态的DIV
    const divStatus = Array.from(fileList.children).find(item =>
        item.querySelector(".file-name").innerHTML === fileName
    )
    // 判断该 DIV 是否存在
    if(divStatus){
        // 找到其后面的 .file-status 这个 DIV
        let updateDiv = divStatus.querySelector(".file-status");
        // 更改文件内容
        updateDiv.innerHTML = message;
        // 更改 class 样式
        updateDiv.className = `file-status status-${status}`
    }
}